home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / components / firebug-service.js < prev    next >
Text File  |  2010-01-15  |  84KB  |  2,746 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. // Debug lines are marked with  at column 120
  4. // Use variable name "fileName" for href returned by JSD, file:/ not same as DOM
  5. // Use variable name "url" for normalizedURL, file:/// comparable to DOM
  6. // Convert from fileName to URL with normalizeURL
  7. // We probably don't need denormalizeURL since we don't send .fileName back to JSD
  8.  
  9. // ************************************************************************************************
  10. // Constants
  11.  
  12. const CLASS_ID = Components.ID("{a380e9c0-cb39-11da-a94d-0800200c9a66}");
  13. const CLASS_NAME = "Firebug Service";
  14. const CONTRACT_ID = "@joehewitt.com/firebug;1";
  15. const Cc = Components.classes;
  16. const Ci = Components.interfaces;
  17.  
  18. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  19.  
  20. const PrefService = Cc["@mozilla.org/preferences-service;1"];
  21. const DebuggerService = Cc["@mozilla.org/js/jsd/debugger-service;1"];
  22. const ConsoleService = Cc["@mozilla.org/consoleservice;1"];
  23. const Timer = Cc["@mozilla.org/timer;1"];
  24. const ObserverServiceFactory = Cc["@mozilla.org/observer-service;1"];
  25.  
  26. const jsdIDebuggerService = Ci.jsdIDebuggerService;
  27. const jsdIScript = Ci.jsdIScript;
  28. const jsdIStackFrame = Ci.jsdIStackFrame;
  29. const jsdICallHook = Ci.jsdICallHook;
  30. const jsdIExecutionHook = Ci.jsdIExecutionHook;
  31. const jsdIErrorHook = Ci.jsdIErrorHook;
  32. const jsdIFilter = Components.interfaces.jsdIFilter;
  33. const nsISupports = Ci.nsISupports;
  34. const nsIPrefBranch = Ci.nsIPrefBranch;
  35. const nsIPrefBranch2 = Ci.nsIPrefBranch2;
  36. const nsIComponentRegistrar = Ci.nsIComponentRegistrar;
  37. const nsIFactory = Ci.nsIFactory;
  38. const nsIConsoleService = Ci.nsIConsoleService;
  39. const nsITimer = Ci.nsITimer;
  40. const nsITimerCallback = Ci.nsITimerCallback;
  41.  
  42. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  43.  
  44. const NS_ERROR_NO_INTERFACE = Components.results.NS_ERROR_NO_INTERFACE;
  45. const NS_ERROR_NOT_IMPLEMENTED = Components.results.NS_ERROR_NOT_IMPLEMENTED;
  46. const NS_ERROR_NO_AGGREGATION = Components.results.NS_ERROR_NO_AGGREGATION;
  47.  
  48. const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT;
  49. const PCMAP_PRETTYPRINT = jsdIScript.PCMAP_PRETTYPRINT;
  50.  
  51. const COLLECT_PROFILE_DATA = jsdIDebuggerService.COLLECT_PROFILE_DATA;
  52. const DISABLE_OBJECT_TRACE = jsdIDebuggerService.DISABLE_OBJECT_TRACE;
  53. const HIDE_DISABLED_FRAMES = jsdIDebuggerService.HIDE_DISABLED_FRAMES;
  54. const DEBUG_WHEN_SET = jsdIDebuggerService.DEBUG_WHEN_SET;
  55. const MASK_TOP_FRAME_ONLY = jsdIDebuggerService.MASK_TOP_FRAME_ONLY;
  56.  
  57. const TYPE_FUNCTION_CALL = jsdICallHook.TYPE_FUNCTION_CALL;
  58. const TYPE_FUNCTION_RETURN = jsdICallHook.TYPE_FUNCTION_RETURN;
  59. const TYPE_TOPLEVEL_START = jsdICallHook.TYPE_TOPLEVEL_START;
  60. const TYPE_TOPLEVEL_END = jsdICallHook.TYPE_TOPLEVEL_END;
  61.  
  62. const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE;
  63. const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL;
  64. const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL;
  65. const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW;
  66.  
  67. const NS_OS_TEMP_DIR = "TmpD"
  68.  
  69. const STEP_OVER = 1;
  70. const STEP_INTO = 2;
  71. const STEP_OUT = 3;
  72. const STEP_SUSPEND = 4;
  73.  
  74. const TYPE_ONE_SHOT = nsITimer.TYPE_ONE_SHOT;
  75.  
  76. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  77.  
  78. const BP_NORMAL = 1;
  79. const BP_MONITOR = 2;
  80. const BP_UNTIL = 4;
  81. const BP_ONRELOAD = 8;  // XXXjjb: This is a mark for the UI to test
  82. const BP_ERROR = 16;
  83. const BP_TRACE = 32; // BP used to initiate traceCalls
  84.  
  85. const LEVEL_TOP = 1;
  86. const LEVEL_EVAL = 2;
  87. const LEVEL_EVENT = 3;
  88.  
  89. const COMPONENTS_FILTERS = [
  90.     new RegExp("^(file:/.*/)extensions/%7B[\\da-fA-F]{8}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{4}-[\\da-fA-F]{12}%7D/components/.*\\.js$"),
  91.     new RegExp("^(file:/.*/)extensions/firebug@software\\.joehewitt\\.com/components/.*\\.js$"),
  92.     new RegExp("^(file:/.*/extensions/)\\w+@mozilla\\.org/components/.*\\.js$"),
  93.     new RegExp("^(file:/.*/components/)ns[A-Z].*\\.js$"),
  94.     new RegExp("^(file:/.*/components/)firebug-[^\\.]*\\.js$"),
  95.     new RegExp("^(file:/.*/Contents/MacOS/extensions/.*/components/).*\\.js$"),
  96.     new RegExp("^(file:/.*/modules/).*\\.jsm$"),
  97.     ];
  98.  
  99. const reDBG = /DBG_(.*)/;
  100. const reXUL = /\.xul$|\.xml$/;
  101. const reTooMuchRecursion = /too\smuch\srecursion/;
  102.  
  103. // ************************************************************************************************
  104. // Globals
  105.  
  106. var jsd, fbs, prefs;
  107. var consoleService;
  108. var observerService;
  109.  
  110. var contextCount = 0;
  111.  
  112. var urlFilters = [
  113.     'chrome://',
  114.     'XStringBundle',
  115.     'x-jsd:ppbuffer?type=function', // internal script for pretty printing
  116.     ];
  117.  
  118.  
  119. var clients = [];
  120. var debuggers = [];
  121. var netDebuggers = [];
  122. var scriptListeners = [];
  123.  
  124. var stepMode = 0;
  125. var stepFrame;
  126. var stepFrameLineId;
  127. var stepStayOnDebuggr; // if set, the debuggr we want to stay within
  128. var stepFrameCount;
  129. var hookFrameCount = 0;
  130.  
  131. var haltDebugger = null;
  132.  
  133. var breakpoints = {};
  134. var breakpointCount = 0;
  135. var disabledCount = 0;
  136. var monitorCount = 0;
  137. var conditionCount = 0;
  138. var runningUntil = null;
  139.  
  140. var errorBreakpoints = [];
  141.  
  142. var profileCount = 0;
  143. var profileStart;
  144.  
  145. var enabledDebugger = false;
  146. var reportNextError = false;
  147. var breakOnNextError = false;
  148. var errorInfo = null;
  149.  
  150. var timer = Timer.createInstance(nsITimer);
  151. var waitingForTimer = false;
  152.  
  153. var FBTrace = null;
  154.  
  155. // ************************************************************************************************
  156.  
  157. function FirebugService()
  158. {
  159.  
  160.     FBTrace = Cc["@joehewitt.com/firebug-trace-service;1"]
  161.                  .getService(Ci.nsISupports).wrappedJSObject.getTracer("extensions.firebug");
  162.  
  163.     fbs = this;
  164.  
  165.     this.wrappedJSObject = this;
  166.     this.timeStamp = new Date();  /* explore */
  167.     this.breakpoints = breakpoints; // so chromebug can see it /* explore */
  168.     this.onDebugRequests = 0;  // the number of times we called onError but did not call onDebug
  169.     fbs._lastErrorDebuggr = null;
  170.  
  171.  
  172.     this.profiling = false;
  173.     this.pauseDepth = 0;
  174.  
  175.     prefs = PrefService.getService(nsIPrefBranch2);
  176.     fbs.prefDomain = "extensions.firebug.service."
  177.     prefs.addObserver(fbs.prefDomain, fbs, false);
  178.  
  179.     observerService = ObserverServiceFactory.getService(Ci.nsIObserverService);
  180.     observerService.addObserver(QuitApplicationGrantedObserver, "quit-application-granted", false);
  181.     observerService.addObserver(QuitApplicationRequestedObserver, "quit-application-requested", false);
  182.     observerService.addObserver(QuitApplicationObserver, "quit-application", false);
  183.  
  184.     this.scriptsFilter = "all";
  185.     // XXXjj For some reason the command line will not function if we allow chromebug to see it.?
  186.     this.alwayFilterURLsStarting = ["chrome://chromebug", "x-jsd:ppbuffer", "chrome://firebug/content/commandLine.js"];  // TODO allow override
  187.     this.onEvalScriptCreated.kind = "eval";
  188.     this.onTopLevelScriptCreated.kind = "top-level";
  189.     this.onEventScriptCreated.kind = "event";
  190.  
  191.     this.onXScriptCreatedByTag = {}; // fbs functions by script tag
  192.     this.nestedScriptStack = Components.classes["@mozilla.org/array;1"]
  193.                         .createInstance(Components.interfaces.nsIMutableArray);  // scripts contained in leveledScript that have not been drained
  194. }
  195.  
  196. FirebugService.prototype =
  197. {
  198.     osOut: function(str)
  199.     {
  200.         if (!this.outChannel)
  201.         {
  202.             try
  203.             {
  204.                 var appShellService = Components.classes["@mozilla.org/appshell/appShellService;1"].
  205.                     getService(Components.interfaces.nsIAppShellService);
  206.                 this.hiddenWindow = appShellService.hiddenDOMWindow;
  207.                 this.outChannel = "hidden";
  208.             }
  209.             catch(exc)
  210.             {
  211.                 consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
  212.                 consoleService.logStringMessage("Using consoleService because nsIAppShellService.hiddenDOMWindow not available "+exc);
  213.                 this.outChannel = "service";
  214.             }
  215.         }
  216.         if (this.outChannel === "hidden")  // apparently can't call via JS function
  217.             this.hiddenWindow.dump(str);
  218.         else
  219.             consoleService.logStringMessage(str);
  220.     },
  221.  
  222.     shutdown: function()  // call disableDebugger first
  223.     {
  224.         timer = null;
  225.  
  226.         if (!jsd)
  227.             return;
  228.  
  229.         try
  230.         {
  231.             do
  232.             {
  233.                 var depth = jsd.exitNestedEventLoop();
  234.             }
  235.             while(depth > 0);
  236.         }
  237.         catch (exc)
  238.         {
  239.             // Seems to be the normal path...FBTrace.sysout("FirebugService, attempt to exitNestedEventLoop fails "+exc);
  240.         }
  241.  
  242.  
  243.         try
  244.         {
  245.             prefs.removeObserver(fbs.prefDomain, fbs);
  246.         }
  247.         catch (exc)
  248.         {
  249.             FBTrace.sysout("fbs prefs.removeObserver fails "+exc, exc);
  250.         }
  251.  
  252.         try
  253.         {
  254.             observerService.removeObserver(QuitApplicationGrantedObserver, "quit-application-granted");
  255.             observerService.removeObserver(QuitApplicationRequestedObserver, "quit-application-requested");
  256.             observerService.removeObserver(QuitApplicationObserver, "quit-application");
  257.         }
  258.         catch (exc)
  259.         {
  260.             FBTrace.sysout("fbs quit-application-observers removeObserver fails "+exc, exc);
  261.         }
  262.  
  263.         jsd = null;
  264.         if (!jsd)
  265.             FBTrace.sysout("*********************** SHUTDOWN JSD NULL ");
  266.     },
  267.  
  268.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  269.     // nsISupports
  270.  
  271.     QueryInterface: function(iid)
  272.     {
  273.         if (!iid.equals(nsISupports))
  274.             throw NS_ERROR_NO_INTERFACE;
  275.  
  276.         return this;
  277.     },
  278.  
  279.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  280.     // nsIObserver
  281.     observe: function(subject, topic, data)
  282.     {
  283.         fbs.obeyPrefs();
  284.     },
  285.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  286.  
  287.     get lastErrorWindow()
  288.     {
  289.         var win = this._lastErrorWindow;
  290.         this._lastErrorWindow = null; // Release to avoid leaks
  291.         return win;
  292.     },
  293.  
  294.     registerClient: function(client)  // clients are essentially XUL windows
  295.     {
  296.         clients.push(client);
  297.         return clients.length;
  298.     },
  299.  
  300.     unregisterClient: function(client)
  301.     {
  302.         for (var i = 0; i < clients.length; ++i)
  303.         {
  304.             if (clients[i] == client)
  305.             {
  306.                 clients.splice(i, 1);
  307.                 break;
  308.             }
  309.         }
  310.     },
  311.  
  312.     registerDebugger: function(debuggrWrapper)  // first one in will be last one called. Returns state enabledDebugger
  313.     {
  314.         var debuggr = debuggrWrapper.wrappedJSObject;
  315.  
  316.         if (debuggr)
  317.         {
  318.             debuggers.push(debuggr);
  319.             if (debuggers.length == 1)
  320.                 this.enableDebugger();
  321.         }
  322.         else
  323.             throw "firebug-service debuggers must have wrappedJSObject";
  324.  
  325.         try {
  326.             if (debuggr.suspendActivity)
  327.                 netDebuggers.push(debuggr);
  328.         } catch(exc) {
  329.         }
  330.         try {
  331.             if (debuggr.onScriptCreated)
  332.                 scriptListeners.push(debuggr);
  333.         } catch(exc) {
  334.         }
  335.         return  debuggers.length;  // 1.3.1 return to allow Debugger to check progress
  336.     },
  337.  
  338.     unregisterDebugger: function(debuggrWrapper)
  339.     {
  340.         var debuggr = debuggrWrapper.wrappedJSObject;
  341.  
  342.         for (var i = 0; i < debuggers.length; ++i)
  343.         {
  344.             if (debuggers[i] == debuggr)
  345.             {
  346.                 debuggers.splice(i, 1);
  347.                 break;
  348.             }
  349.         }
  350.  
  351.         for (var i = 0; i < netDebuggers.length; ++i)
  352.         {
  353.             if (netDebuggers[i] == debuggr)
  354.             {
  355.                 netDebuggers.splice(i, 1);
  356.                 break;
  357.             }
  358.         }
  359.         for (var i = 0; i < scriptListeners.length; ++i)
  360.         {
  361.             if (scriptListeners[i] == debuggr)
  362.             {
  363.                 scriptListeners.splice(i, 1);
  364.                 break;
  365.             }
  366.         }
  367.  
  368.         if (debuggers.length == 0)
  369.             this.disableDebugger();
  370.  
  371.         return debuggers.length;
  372.     },
  373.  
  374.     lockDebugger: function()
  375.     {
  376.         if (this.locked)
  377.             return;
  378.  
  379.         this.locked = true;
  380.  
  381.         dispatch(debuggers, "onLock", [true]);
  382.     },
  383.  
  384.     unlockDebugger: function()
  385.     {
  386.         if (!this.locked)
  387.             return;
  388.  
  389.         this.locked = false;
  390.  
  391.         dispatch(debuggers, "onLock", [false]);
  392.     },
  393.  
  394.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  395.     forceGarbageCollection: function()
  396.     {
  397.         jsd.GC(); // Force the engine to perform garbage collection.
  398.     },
  399.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  400.  
  401.     enterNestedEventLoop: function(callback)
  402.     {
  403.         dispatch(netDebuggers, "suspendActivity");
  404.         fbs.nestedEventLoopDepth = jsd.enterNestedEventLoop({
  405.             onNest: function()
  406.             {
  407.                 dispatch(netDebuggers, "resumeActivity");
  408.                 callback.onNest();
  409.             }
  410.         });
  411.         dispatch(netDebuggers, "resumeActivity");
  412.         return fbs.nestedEventLoopDepth;
  413.     },
  414.  
  415.     exitNestedEventLoop: function()
  416.     {
  417.         dispatch(netDebuggers, "suspendActivity");
  418.         try
  419.         {
  420.             return jsd.exitNestedEventLoop();
  421.         }
  422.         catch (exc)
  423.         {
  424.         }
  425.     },
  426.  
  427.     halt: function(debuggr)
  428.     {
  429.         haltDebugger = debuggr;
  430.     },
  431.  
  432.     step: function(mode, startFrame, stayOnDebuggr)
  433.     {
  434.         stepMode = mode;
  435.         stepFrame = startFrame;
  436.         stepFrameCount = countFrames(startFrame);
  437.         stepFrameLineId = stepFrameCount + startFrame.script.fileName + startFrame.line;
  438.         stepStayOnDebuggr = stayOnDebuggr;
  439.  
  440.     },
  441.  
  442.     suspend: function(stayOnDebuggr, context)
  443.     {
  444.         stepMode = STEP_SUSPEND;
  445.         stepFrameLineId = null;
  446.         stepStayOnDebuggr = stayOnDebuggr;
  447.  
  448.         dispatch(debuggers, "onBreakingNext", [stayOnDebuggr, context]);
  449.  
  450.         this.hookInterrupts();
  451.     },
  452.  
  453.     runUntil: function(sourceFile, lineNo, startFrame, debuggr)
  454.     {
  455.         runningUntil = this.addBreakpoint(BP_UNTIL, sourceFile, lineNo, null, debuggr);
  456.         stepFrameCount = countFrames(startFrame);
  457.         stepFrameLineId = stepFrameCount + startFrame.script.fileName + startFrame.line;
  458.     },
  459.  
  460.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  461.  
  462.     setBreakpoint: function(sourceFile, lineNo, props, debuggr)
  463.     {
  464.         var bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, props, debuggr);
  465.         if (bp)
  466.         {
  467.             dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]);
  468.             return true;
  469.         }
  470.         return false;
  471.     },
  472.  
  473.     clearBreakpoint: function(url, lineNo)
  474.     {
  475.         var bp = this.removeBreakpoint(BP_NORMAL, url, lineNo);
  476.         if (bp)
  477.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, false, bp]);
  478.         else
  479.         {
  480.         }
  481.     },
  482.  
  483.     enableBreakpoint: function(url, lineNo)
  484.     {
  485.         var bp = this.findBreakpoint(url, lineNo);
  486.         if (bp && bp.type & BP_NORMAL)
  487.         {
  488.             bp.disabled &= ~BP_NORMAL;
  489.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]);
  490.             --disabledCount;
  491.         }
  492.         else {
  493.         }
  494.     },
  495.  
  496.     disableBreakpoint: function(url, lineNo)
  497.     {
  498.         var bp = this.findBreakpoint(url, lineNo);
  499.         if (bp && bp.type & BP_NORMAL)
  500.         {
  501.             bp.disabled |= BP_NORMAL;
  502.             ++disabledCount;
  503.             dispatch(debuggers, "onToggleBreakpoint", [url, lineNo, true, bp]);
  504.         }
  505.         else
  506.         {
  507.         }
  508.  
  509.     },
  510.  
  511.     isBreakpointDisabled: function(url, lineNo)
  512.     {
  513.         var bp = this.findBreakpoint(url, lineNo);
  514.         if (bp && bp.type & BP_NORMAL)
  515.             return bp.disabled & BP_NORMAL;
  516.         else
  517.             return false;
  518.     },
  519.  
  520.     setBreakpointCondition: function(sourceFile, lineNo, condition, debuggr)
  521.     {
  522.         var bp = this.findBreakpoint(sourceFile.href, lineNo);
  523.         if (!bp)
  524.         {
  525.             bp = this.addBreakpoint(BP_NORMAL, sourceFile, lineNo, null, debuggr);
  526.         }
  527.  
  528.         if (!bp)
  529.             return;
  530.  
  531.         if (bp.hitCount <= 0 )
  532.         {
  533.             if (bp.condition && !condition)
  534.             {
  535.                 --conditionCount;
  536.             }
  537.             else if (condition && !bp.condition)
  538.             {
  539.                 ++conditionCount;
  540.             }
  541.         }
  542.         bp.condition = condition;
  543.  
  544.         dispatch(debuggers, "onToggleBreakpoint", [sourceFile.href, lineNo, true, bp]);
  545.     },
  546.  
  547.     getBreakpointCondition: function(url, lineNo)
  548.     {
  549.         var bp = this.findBreakpoint(url, lineNo);
  550.         return bp ? bp.condition : "";
  551.     },
  552.  
  553.     clearAllBreakpoints: function(sourceFiles)
  554.     {
  555.         for (var i = 0; i < sourceFiles.length; ++i)
  556.         {
  557.             var url = sourceFiles[i].href;
  558.             if (!url)
  559.                 continue;
  560.  
  561.             var urlBreakpoints = breakpoints[url];
  562.             if (!urlBreakpoints)
  563.                 continue;
  564.  
  565.             var removals = urlBreakpoints.length;
  566.             for (var j = 0; j < removals; ++j)
  567.             {
  568.                 var bp = urlBreakpoints[0];  // this one will be spliced out each time
  569.                 this.clearBreakpoint(url, bp.lineNo);
  570.             }
  571.          }
  572.     },
  573.  
  574.     enumerateBreakpoints: function(url, cb)  // url is sourceFile.href, not jsd script.fileName
  575.     {
  576.         if (url)
  577.         {
  578.             var urlBreakpoints = breakpoints[url];
  579.             if (urlBreakpoints)
  580.             {
  581.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  582.                 {
  583.                     var bp = urlBreakpoints[i];
  584.                     if (bp.type & BP_NORMAL)
  585.                     {
  586.                         if (bp.scriptsWithBreakpoint && bp.scriptsWithBreakpoint.length > 0)
  587.                         {
  588.                             for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  589.                             {
  590.                                 var rc = cb.call(url, bp.lineNo, bp, bp.scriptsWithBreakpoint[j]);
  591.                                 if (rc)
  592.                                     return [bp];
  593.                             }
  594.                         } else {
  595.                             var rc = cb.call(url, bp.lineNo, bp);
  596.                             if (rc)
  597.                                 return [bp];
  598.                         }
  599.                     }
  600.                 }
  601.             }
  602.         }
  603.         else
  604.         {
  605.             var bps = [];
  606.             for (var url in breakpoints)
  607.                 bps.push(this.enumerateBreakpoints(url, cb));
  608.             return bps;
  609.         }
  610.     },
  611.  
  612.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  613.     // error breakpoints are a way of selecting breakpoint from the Console
  614.     //
  615.     setErrorBreakpoint: function(sourceFile, lineNo, debuggr)
  616.     {
  617.         var url = sourceFile.href;
  618.         var index = this.findErrorBreakpoint(url, lineNo);
  619.         if (index == -1)
  620.         {
  621.             this.setBreakpoint(sourceFile, lineNo, null, debuggr);
  622.             errorBreakpoints.push({href: url, lineNo: lineNo, type: BP_ERROR });
  623.             dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, true, debuggr]);
  624.         }
  625.     },
  626.  
  627.     clearErrorBreakpoint: function(sourceFile, lineNo, debuggr)
  628.     {
  629.         var url = sourceFile.href;
  630.         var index = this.findErrorBreakpoint(url, lineNo);
  631.         if (index != -1)
  632.         {
  633.             this.clearBreakpoint(url, lineNo);
  634.             errorBreakpoints.splice(index, 1);
  635.  
  636.             dispatch(debuggers, "onToggleErrorBreakpoint", [url, lineNo, false, debuggr]);
  637.         }
  638.     },
  639.  
  640.     hasErrorBreakpoint: function(url, lineNo)
  641.     {
  642.         return this.findErrorBreakpoint(url, lineNo) != -1;
  643.     },
  644.  
  645.     enumerateErrorBreakpoints: function(url, cb)
  646.     {
  647.         if (url)
  648.         {
  649.             for (var i = 0; i < errorBreakpoints.length; ++i)
  650.             {
  651.                 var bp = errorBreakpoints[i];
  652.                 if (bp.href == url)
  653.                     cb.call(bp.href, bp.lineNo, bp);
  654.             }
  655.         }
  656.         else
  657.         {
  658.             for (var i = 0; i < errorBreakpoints.length; ++i)
  659.             {
  660.                 var bp = errorBreakpoints[i];
  661.                 cb.call(bp.href, bp.lineNo, bp);
  662.             }
  663.         }
  664.     },
  665.  
  666.     findErrorBreakpoint: function(url, lineNo)
  667.     {
  668.         for (var i = 0; i < errorBreakpoints.length; ++i)
  669.         {
  670.             var bp = errorBreakpoints[i];
  671.             if (bp.lineNo == lineNo && bp.href == url)
  672.                 return i;
  673.         }
  674.  
  675.         return -1;
  676.     },
  677.  
  678.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  679.  
  680.     traceAll: function(urls, debuggr)
  681.     {
  682.         this.hookCalls(debuggr.onFunctionCall, false);  // call on all passed urls
  683.     },
  684.  
  685.     untraceAll: function(debuggr)
  686.     {
  687.         jsd.functionHook = null; // undo hookCalls()
  688.     },
  689.  
  690.     traceCalls: function(sourceFile, lineNo, debuggr)
  691.     {
  692.         var bp = this.monitor(sourceFile, lineNo, debuggr); // set a breakpoint on the starting point
  693.         bp.type |= BP_TRACE;
  694.         // when we hit the bp in onBreakPoint we being tracing.
  695.     },
  696.  
  697.     untraceCalls: function(sourceFile, lineNo, debuggr)
  698.     {
  699.         var bp = lineNo != -1 ? this.findBreakpoint(url, lineNo) : null;
  700.         if (bp)
  701.         {
  702.             bp.type &= ~BP_TRACE;
  703.             this.unmonitor(sourceFile, lineNo);
  704.         }
  705.     },
  706.  
  707.     monitor: function(sourceFile, lineNo, debuggr)
  708.     {
  709.         if (lineNo == -1)
  710.             return null;
  711.  
  712.         var bp = this.addBreakpoint(BP_MONITOR, sourceFile, lineNo, null, debuggr);
  713.         if (bp)
  714.         {
  715.             ++monitorCount;
  716.             dispatch(debuggers, "onToggleMonitor", [sourceFile.href, lineNo, true]);
  717.         }
  718.         return bp;
  719.     },
  720.  
  721.     unmonitor: function(sourceFile, lineNo)
  722.     {
  723.         if (lineNo != -1 && this.removeBreakpoint(BP_MONITOR, sourceFile.href, lineNo))
  724.         {
  725.             --monitorCount;
  726.             dispatch(debuggers, "onToggleMonitor", [sourceFile.href, lineNo, false]);
  727.         }
  728.     },
  729.  
  730.     isMonitored: function(url, lineNo)
  731.     {
  732.         var bp = lineNo != -1 ? this.findBreakpoint(url, lineNo) : null;
  733.         return bp && bp.type & BP_MONITOR;
  734.     },
  735.  
  736.     enumerateMonitors: function(url, cb)
  737.     {
  738.         if (url)
  739.         {
  740.             var urlBreakpoints = breakpoints[url];
  741.             if (urlBreakpoints)
  742.             {
  743.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  744.                 {
  745.                     var bp = urlBreakpoints[i];
  746.                     if (bp.type & BP_MONITOR)
  747.                         cb.call(url, bp.lineNo, bp);
  748.                 }
  749.             }
  750.         }
  751.         else
  752.         {
  753.             for (var url in breakpoints)
  754.                 this.enumerateBreakpoints(url, cb);
  755.         }
  756.     },
  757.  
  758.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  759.  
  760.     enumerateScripts: function(length)
  761.     {
  762.         var scripts = [];
  763.         jsd.enumerateScripts( {
  764.             enumerateScript: function(script) {
  765.                 var fileName = script.fileName;
  766.                 if ( !isFilteredURL(fileName) ) {
  767.                     scripts.push(script);
  768.                 }
  769.             }
  770.         });
  771.         length.value = scripts.length;
  772.         return scripts;
  773.     },
  774.  
  775.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  776.  
  777.     startProfiling: function()
  778.     {
  779.         if (!this.profiling)
  780.         {
  781.             this.profiling = true;
  782.             profileStart = new Date();
  783.  
  784.             jsd.flags |= COLLECT_PROFILE_DATA;
  785.         }
  786.  
  787.         ++profileCount;
  788.     },
  789.  
  790.     stopProfiling: function()
  791.     {
  792.         if (--profileCount == 0)
  793.         {
  794.             jsd.flags &= ~COLLECT_PROFILE_DATA;
  795.  
  796.             var t = profileStart.getTime();
  797.  
  798.             this.profiling = false;
  799.             profileStart = null;
  800.  
  801.             return new Date().getTime() - t;
  802.         }
  803.         else
  804.             return -1;
  805.     },
  806.  
  807.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  808.  
  809.     enableDebugger: function()
  810.     {
  811.         if (waitingForTimer)
  812.         {
  813.             timer.cancel();
  814.             waitingForTimer = false;
  815.         }
  816.         if (enabledDebugger)
  817.             return;
  818.  
  819.         enabledDebugger = true;
  820.  
  821.         this.obeyPrefs();
  822.  
  823.         if (!jsd)
  824.         {
  825.             jsd = DebuggerService.getService(jsdIDebuggerService);
  826.  
  827.         }
  828.  
  829.         if (!jsd.isOn)
  830.         {
  831.             jsd.on(); // this should be the only call to jsd.on().
  832.             jsd.flags |= DISABLE_OBJECT_TRACE;
  833.  
  834.             if (jsd.pauseDepth && FBTrace.DBG_FBS_ERRORS)
  835.                 FBTrace.sysout("fbs.enableDebugger found non-zero jsd.pauseDepth !! "+jsd.pauseDepth)
  836.         }
  837.  
  838.         if (!this.filterChrome)
  839.             this.createChromeBlockingFilters();
  840.  
  841.         var active = fbs.isJSDActive();
  842.  
  843.         dispatch(clients, "onJSDActivate", [active, "fbs enableDebugger"]);
  844.         this.hookScripts();
  845.     },
  846.  
  847.     obeyPrefs: function()
  848.     {
  849.         this.showStackTrace = prefs.getBoolPref("extensions.firebug.service.showStackTrace");
  850.         this.breakOnErrors = prefs.getBoolPref("extensions.firebug.service.breakOnErrors");
  851.         this.trackThrowCatch = prefs.getBoolPref("extensions.firebug.service.trackThrowCatch");
  852.         this.scriptsFilter = prefs.getCharPref("extensions.firebug.service.scriptsFilter");
  853.         this.filterSystemURLs = prefs.getBoolPref("extensions.firebug.service.filterSystemURLs");  // may not be exposed to users
  854.  
  855.         FirebugPrefsObserver.syncFilter();
  856.  
  857.         try {
  858.             // CREATION and BP generate a huge trace
  859.         }
  860.         catch (exc)
  861.         {
  862.             FBTrace.sysout("firebug-service: constructor getBoolPrefs FAILED with exception=",exc);
  863.         }
  864.     },
  865.  
  866.     disableDebugger: function()
  867.     {
  868.         if (!enabledDebugger)
  869.             return;
  870.  
  871.         if (!timer)  // then we probably shutdown
  872.             return;
  873.  
  874.         enabledDebugger = false;
  875.  
  876.         if (jsd.isOn)
  877.         {
  878.             jsd.pause();
  879.             fbs.unhookScripts();
  880.  
  881.             while (jsd.pauseDepth > 0)  // unwind completely
  882.                 jsd.unPause();
  883.             fbs.pauseDepth = 0;
  884.  
  885.             jsd.off();
  886.         }
  887.  
  888.         var active = fbs.isJSDActive();
  889.         dispatch(clients, "onJSDDeactivate", [active, "fbs disableDebugger"]);
  890.  
  891.         fbs.onXScriptCreatedByTag = {};  // clear any uncleared top level scripts
  892.  
  893.     },
  894.  
  895.     pause: function()  // must support multiple calls
  896.     {
  897.         if (!enabledDebugger)
  898.             return "not enabled";
  899.         var rejection = [];
  900.         dispatch(clients, "onPauseJSDRequested", [rejection]);
  901.  
  902.         if (rejection.length == 0)  // then everyone wants to pause
  903.         {
  904.             if (fbs.pauseDepth == 0)  // don't pause if we are paused.
  905.             {
  906.                 fbs.pauseDepth++;
  907.                 jsd.pause();
  908.                 fbs.unhookScripts();
  909.             }
  910.             var active = fbs.isJSDActive();
  911.             dispatch(clients, "onJSDDeactivate", [active, "pause depth "+jsd.pauseDepth]);
  912.         }
  913.         else // we don't want to pause
  914.         {
  915.             while (fbs.pauseDepth > 0)  // make sure we are not paused.
  916.                 fbs.unPause();
  917.             fbs.pauseDepth = 0;
  918.         }
  919.         return fbs.pauseDepth;
  920.     },
  921.  
  922.     unPause: function()
  923.     {
  924.         if (fbs.pauseDepth > 0)
  925.         {
  926.             fbs.pauseDepth--;
  927.             fbs.hookScripts();
  928.  
  929.             var depth = jsd.unPause();
  930.             var active = fbs.isJSDActive();
  931.  
  932.  
  933.             dispatch(clients, "onJSDActivate", [active, "unpause depth"+jsd.pauseDepth]);
  934.  
  935.         }
  936.         else  // we were not paused.
  937.         {
  938.         }
  939.         return fbs.pauseDepth;
  940.     },
  941.  
  942.     isJSDActive: function()
  943.     {
  944.         return (jsd && jsd.isOn && (jsd.pauseDepth == 0) );
  945.     },
  946.  
  947.     broadcast: function(message, args)  // re-transmit the message (string) with args [objs] to XUL windows.
  948.     {
  949.         dispatch(clients, message, args);
  950.     },
  951.  
  952.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  953.  
  954.     normalizeURL: function(url)
  955.     {
  956.         // For some reason, JSD reports file URLs like "file:/" instead of "file:///", so they
  957.         // don't match up with the URLs we get back from the DOM
  958.         return url ? url.replace(/file:\/([^/])/, "file:///$1") : "";
  959.     },
  960.  
  961.     denormalizeURL: function(url)
  962.     {
  963.         // This should not be called.
  964.         return url ? url.replace(/file:\/\/\//, "file:/") : "";
  965.     },
  966.  
  967.  
  968.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  969.     // jsd Hooks
  970.  
  971.     // When (debugger keyword and not halt)||(bp and BP_UNTIL) || (onBreakPoint && no conditions)
  972.     // || interuptHook.  rv is ignored
  973.     onBreak: function(frame, type, rv)
  974.     {
  975.         try
  976.         {
  977.             // avoid step_out from web page to chrome
  978.             if (type==jsdIExecutionHook.TYPE_INTERRUPTED && stepStayOnDebuggr)
  979.             {
  980.                 var debuggr = this.reFindDebugger(frame, stepStayOnDebuggr);
  981.                 if (!debuggr)
  982.                 {
  983.                     // This frame is not for the debugger we want
  984.                     if (stepMode == STEP_OVER || stepMode == STEP_OUT)  // then we are in the debuggr we want and returned in to one we don't
  985.                     {
  986.                         this.stopStepping(); // run, you are free.
  987.                     }
  988.  
  989.                     return RETURN_CONTINUE;  // This means that we will continue to take interrupts until  when?
  990.                 }
  991.                 else
  992.                 {
  993.                     if (stepMode == STEP_SUSPEND) // then we have interrupted the outerFunction
  994.                     {
  995.                         var scriptTag = frame.script.tag;
  996.                         if (scriptTag in this.onXScriptCreatedByTag) // yes, we have to create the sourceFile
  997.                             this.onBreakpoint(frame, type, rv);  // TODO refactor so we don't get mixed up
  998.                     }
  999.                 }
  1000.             }
  1001.             else
  1002.             {
  1003.                 var debuggr = this.findDebugger(frame);
  1004.  
  1005.             }
  1006.  
  1007.             if (debuggr)
  1008.                 return this.breakIntoDebugger(debuggr, frame, type);
  1009.  
  1010.         }
  1011.         catch(exc)
  1012.         {
  1013.             ERROR("onBreak failed: "+exc);
  1014.         }
  1015.         return RETURN_CONTINUE;
  1016.     },
  1017.  
  1018.     // When engine encounters debugger keyword (only)
  1019.     onDebugger: function(frame, type, rv)
  1020.     {
  1021.         try {
  1022.             if (haltDebugger)
  1023.             {
  1024.                 var debuggr = haltDebugger;
  1025.                 haltDebugger = null;
  1026.                 return debuggr.onHalt(frame);
  1027.             }
  1028.             else
  1029.                 return this.onBreak(frame, type, rv);
  1030.          }
  1031.          catch(exc)
  1032.          {
  1033.             ERROR("onDebugger failed: "+exc);
  1034.             return RETURN_CONTINUE;
  1035.          }
  1036.     },
  1037.  
  1038.     // when the onError handler returns false
  1039.     onDebug: function(frame, type, rv)
  1040.     {
  1041.         if ( isFilteredURL(frame.script.fileName) )
  1042.             return RETURN_CONTINUE;
  1043.         try
  1044.         {
  1045.             var debuggr = (reportNextError || breakOnNextError) ? this.findDebugger(frame) : null;
  1046.  
  1047.             if (reportNextError)
  1048.             {
  1049.                 reportNextError = false;
  1050.                 if (debuggr)
  1051.                 {
  1052.                     var hookReturn = debuggr.onError(frame, errorInfo);
  1053.                     if (hookReturn >=0)
  1054.                         return hookReturn;
  1055.                     else if (hookReturn==-1)
  1056.                         breakOnNextError = true;
  1057.                     if (breakOnNextError)
  1058.                         debuggr = this.reFindDebugger(frame, debuggr);
  1059.                 }
  1060.             }
  1061.  
  1062.             if (breakOnNextError)
  1063.             {
  1064.                 breakOnNextError = false;
  1065.                 if (debuggr)
  1066.                     return this.breakIntoDebugger(debuggr, frame, type);
  1067.             }
  1068.         } catch (exc) {
  1069.             ERROR("onDebug failed: "+exc);
  1070.         }
  1071.         return RETURN_CONTINUE;
  1072.     },
  1073.  
  1074.     onBreakpoint: function(frame, type, val)
  1075.     {
  1076.         var scriptTag = frame.script.tag;
  1077.         if (scriptTag in this.onXScriptCreatedByTag)
  1078.         {
  1079.             var onXScriptCreated = this.onXScriptCreatedByTag[scriptTag];
  1080.             delete this.onXScriptCreatedByTag[scriptTag];
  1081.             frame.script.clearBreakpoint(0);
  1082.             try {
  1083.                 var sourceFile = onXScriptCreated(frame, type, val);
  1084.             } catch (e) {
  1085.                 FBTrace.sysout("onBreakpoint called onXScriptCreated and it didn't end well:",e);
  1086.             }
  1087.  
  1088.             if (!sourceFile || !sourceFile.breakOnZero || sourceFile.breakOnZero != scriptTag)
  1089.                 return RETURN_CONTINUE;
  1090.             else  // sourceFile.breakOnZero matches the script we have halted.
  1091.             {
  1092.             }
  1093.         }
  1094.  
  1095.  
  1096.         var bp = this.findBreakpointByScript(frame.script, frame.pc);
  1097.         if (bp)
  1098.         {
  1099.             // See issue 1179, should not break if we resumed from a single step and have not advanced.
  1100.             if (disabledCount || monitorCount || conditionCount || runningUntil)
  1101.             {
  1102.                 if (bp.type & BP_MONITOR && !(bp.disabled & BP_MONITOR))
  1103.                 {
  1104.                     if (bp.type & BP_TRACE && !(bp.disabled & BP_TRACE) )
  1105.                         this.hookCalls(bp.debugger.onFunctionCall, true);
  1106.                     else
  1107.                         bp.debugger.onMonitorScript(frame);
  1108.                 }
  1109.  
  1110.                 if (bp.type & BP_UNTIL)
  1111.                 {
  1112.                     this.stopStepping();
  1113.                     if (bp.debugger)
  1114.                         return this.breakIntoDebugger(bp.debugger, frame, type);
  1115.                 }
  1116.                 else if (!(bp.type & BP_NORMAL) || bp.disabled & BP_NORMAL)
  1117.                 {
  1118.                     return  RETURN_CONTINUE;
  1119.                 }
  1120.                 else if (bp.type & BP_NORMAL)
  1121.                 {
  1122.                     var passed = testBreakpoint(frame, bp);
  1123.                     if (!passed)
  1124.                         return RETURN_CONTINUE;
  1125.                 }
  1126.                 // type was normal, but passed test
  1127.             }
  1128.             else  // not special, just break for sure
  1129.                 return this.breakIntoDebugger(bp.debugger, frame, type);
  1130.         }
  1131.         else
  1132.         {
  1133.         }
  1134.  
  1135.         if (runningUntil)
  1136.             return RETURN_CONTINUE;
  1137.         else
  1138.             return this.onBreak(frame, type, val);
  1139.     },
  1140.  
  1141.     onThrow: function(frame, type, rv)
  1142.     {
  1143.         if ( isFilteredURL(frame.script.fileName) )
  1144.             return RETURN_CONTINUE_THROW;
  1145.  
  1146.         if (rv && rv.value && rv.value.isValid)
  1147.         {
  1148.             var value = rv.value;
  1149.             if (value.jsClassName == "Error" && reTooMuchRecursion.test(value.stringValue))
  1150.             {
  1151.                 if (fbs._lastErrorCaller)
  1152.                 {
  1153.                     if (fbs._lastErrorCaller == frame.script.tag) // then are unwinding recursion
  1154.                     {
  1155.                         fbs._lastErrorCaller = frame.callingFrame ? frame.callingFrame.script.tag : null;
  1156.                         return RETURN_CONTINUE_THROW;
  1157.                     }
  1158.                 }
  1159.                 else
  1160.                 {
  1161.                     fbs._lastErrorCaller = frame.callingFrame.script.tag;
  1162.                     frame = fbs.discardRecursionFrames(frame);
  1163.                     // go on to process the throw.
  1164.                 }
  1165.             }
  1166.             else
  1167.             {
  1168.                 delete fbs._lastErrorCaller; // throw is not recursion
  1169.             }
  1170.         }
  1171.         else
  1172.         {
  1173.             delete fbs._lastErrorCaller; // throw is not recursion either
  1174.         }
  1175.  
  1176.         // Remember the error where the last exception is thrown - this will
  1177.         // be used later when the console service reports the error, since
  1178.         // it doesn't currently report the window where the error occurred
  1179.  
  1180.         this._lastErrorWindow =  getFrameGlobal(frame);
  1181.  
  1182.         if (this.showStackTrace)  // store these in case the throw is not caught
  1183.         {
  1184.             var debuggr = this.findDebugger(frame);  // sets debuggr.breakContext
  1185.             if (debuggr)
  1186.             {
  1187.                 fbs._lastErrorScript = frame.script;
  1188.                 fbs._lastErrorLine = frame.line;
  1189.                 fbs._lastErrorDebuggr = debuggr;
  1190.                 fbs._lastErrorContext = debuggr.breakContext; // XXXjjb this is bad API
  1191.             }
  1192.             else
  1193.                 delete fbs._lastErrorDebuggr;
  1194.         }
  1195.  
  1196.         if (fbs.trackThrowCatch)
  1197.         {
  1198.             var debuggr = this.findDebugger(frame);
  1199.             if (debuggr)
  1200.                 return debuggr.onThrow(frame, rv);
  1201.         }
  1202.  
  1203.         return RETURN_CONTINUE_THROW;
  1204.     },
  1205.  
  1206.     onError: function(message, fileName, lineNo, pos, flags, errnum, exc)
  1207.     {
  1208.         errorInfo = { errorMessage: message, sourceName: fileName, lineNumber: lineNo,
  1209.                 message: message, fileName: fileName, lineNo: lineNo,
  1210.                 columnNumber: pos, flags: flags, category: "js", errnum: errnum, exc: exc };
  1211.  
  1212.         if (message=="out of memory")  // bail
  1213.         {
  1214.             return true;
  1215.         }
  1216.  
  1217.         if (this.showStackTrace)
  1218.         {
  1219.             reportNextError = true;
  1220.             return false; // Drop into onDebug, sometimes only
  1221.         }
  1222.         else
  1223.         {
  1224.             return !this.needToBreakForError(fileName, lineNo);
  1225.         }
  1226.     },
  1227.  
  1228.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1229.  
  1230.     onEventScriptCreated: function(frame, type, val, noNestTest)
  1231.     {
  1232.         if (fbs.showEvents)
  1233.         {
  1234.             try
  1235.             {
  1236.                if (!noNestTest)
  1237.                 {
  1238.                     // In onScriptCreated we saw a script with baseLineNumber = 1. We marked it as event and nested.
  1239.                     // Now we know its event, not nested.
  1240.                     if (fbs.nestedScriptStack.length > 0)
  1241.                     {
  1242.                         fbs.nestedScriptStack.removeElementAt(0);
  1243.                     }
  1244.                     else
  1245.                     {
  1246.                     }
  1247.                 }
  1248.  
  1249.                 var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1250.                 if (debuggr)
  1251.                 {
  1252.                     var sourceFile = debuggr.onEventScriptCreated(frame, frame.script, fbs.nestedScriptStack.enumerate());
  1253.                     fbs.resetBreakpoints(sourceFile);
  1254.                 }
  1255.                 else
  1256.                 {
  1257.                 }
  1258.             } catch(exc) {
  1259.             }
  1260.         }
  1261.  
  1262.         fbs.clearNestedScripts();
  1263.         return sourceFile;
  1264.     },
  1265.  
  1266.     onEvalScriptCreated: function(frame, type, val)
  1267.     {
  1268.         if (fbs.showEvals)
  1269.         {
  1270.             try
  1271.             {
  1272.                 if (!frame.callingFrame)
  1273.                 {
  1274.                     return fbs.onEventScriptCreated(frame, type, val, true);
  1275.                 }
  1276.                 // In onScriptCreated we found a no-name script, set a bp in PC=0, and a flag.
  1277.                 // onBreakpoint saw the flag, cleared the flag, and sent us here.
  1278.                 // Start by undoing our damage
  1279.                 var outerScript = frame.script;
  1280.  
  1281.                 var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1282.                 if (debuggr)
  1283.                 {
  1284.                     var sourceFile = debuggr.onEvalScriptCreated(frame, outerScript, fbs.nestedScriptStack.enumerate());
  1285.                     fbs.resetBreakpoints(sourceFile);
  1286.                 }
  1287.                 else
  1288.                 {
  1289.                 }
  1290.             }
  1291.             catch (exc)
  1292.             {
  1293.                 ERROR("onEvalScriptCreated failed: "+exc);
  1294.             }
  1295.         }
  1296.  
  1297.         fbs.clearNestedScripts();
  1298.         return sourceFile;
  1299.     },
  1300.  
  1301.     onTopLevelScriptCreated: function(frame, type, val)
  1302.     {
  1303.         try
  1304.         {
  1305.             // In onScriptCreated we may have found a script at baseLineNumber=1
  1306.             // Now we know its not an event
  1307.             if (fbs.nestedScriptStack.length > 0)
  1308.             {
  1309.                 var firstScript = fbs.nestedScriptStack.queryElementAt(0, jsdIScript);
  1310.                 if (firstScript.tag in fbs.onXScriptCreatedByTag)
  1311.                 {
  1312.                     delete  fbs.onXScriptCreatedByTag[firstScript.tag];
  1313.                     if (firstScript.isValid)
  1314.                         firstScript.clearBreakpoint(0);
  1315.                 }
  1316.             }
  1317.  
  1318.             // On compilation of a top-level (global-appending) function.
  1319.             // After this top-level script executes we lose the jsdIScript so we can't build its line table.
  1320.             // Therefore we need to build it here.
  1321.             var debuggr = fbs.findDebugger(frame);  // sets debuggr.breakContext
  1322.             if (debuggr)
  1323.             {
  1324.                 var sourceFile = debuggr.onTopLevelScriptCreated(frame, frame.script, fbs.nestedScriptStack.enumerate());
  1325.                 fbs.resetBreakpoints(sourceFile, frame.script.baseLineNumber+frame.script.lineExtent);
  1326.             }
  1327.             else
  1328.             {
  1329.                 // modules end up here?
  1330.             }
  1331.         }
  1332.         catch (exc)
  1333.         {
  1334.             FBTrace.sysout("onTopLevelScriptCreated FAILED: ", exc);
  1335.             ERROR("onTopLevelScriptCreated Fails: "+exc);
  1336.         }
  1337.  
  1338.         fbs.clearNestedScripts();
  1339.         return sourceFile;
  1340.     },
  1341.  
  1342.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1343.  
  1344.     clearNestedScripts: function()
  1345.     {
  1346.         var innerScripts = fbs.nestedScriptStack.enumerate();
  1347.         while (innerScripts.hasMoreElements())
  1348.         {
  1349.             var script = innerScripts.getNext();
  1350.             if (script.isValid && script.baseLineNumber == 1)
  1351.             {
  1352.                 script.clearBreakpoint(0);
  1353.                 if (this.onXScriptCreatedByTag[script.tag])
  1354.                     delete this.onXScriptCreatedByTag[script.tag];
  1355.             }
  1356.         }
  1357.         fbs.nestedScriptStack.clear();
  1358.     },
  1359.  
  1360.     onScriptCreated: function(script)
  1361.     {
  1362.         if (!fbs)
  1363.         {
  1364.              return;
  1365.         }
  1366.  
  1367.         try
  1368.         {
  1369.             var fileName = script.fileName;
  1370.             if (isFilteredURL(fileName))
  1371.             {
  1372.                 try {
  1373.                 } catch (exc) { /*Bug 426692 */ }
  1374.                 return;
  1375.             }
  1376.  
  1377.             // reset tracing flags on first unfiltered filename
  1378.             if (!FBTrace.DBG_FF_START && !fbs.firstUnfilteredFilename)
  1379.             {
  1380.                 fbs.firstUnfilteredFilename = true;
  1381.                 FBTrace.DBG_FBS_BP = fbs.resetBP ? true : false;
  1382.                 FBTrace.DBG_FBS_CREATION = fbs.resetCreation ? true : false;
  1383.             }
  1384.  
  1385.             if (fbs.pendingXULFileName && fbs.pendingXULFileName != script.fileName)
  1386.                 fbs.flushXUL();
  1387.  
  1388.             if (!script.functionName) // top or eval-level
  1389.             {
  1390.                 // We need to detect eval() and grab its source.
  1391.                 var hasCaller = fbs.createdScriptHasCaller();
  1392.                 if (hasCaller)
  1393.                 {
  1394.                     // components end up here
  1395.                     fbs.onXScriptCreatedByTag[script.tag] = this.onEvalScriptCreated;
  1396.                 }
  1397.                 else
  1398.                     fbs.onXScriptCreatedByTag[script.tag] = this.onTopLevelScriptCreated;
  1399.  
  1400.                 script.setBreakpoint(0);
  1401.             }
  1402.             else if (script.baseLineNumber == 1)
  1403.             {
  1404.                 // could be a 1) Browser-generated event handler or 2) a nested script at the top of a file
  1405.                 // One way to tell is assume both then wait to see which we hit first:
  1406.                 // 1) bp at pc=0 for this script or 2) for a top-level on at the same filename
  1407.  
  1408.                 fbs.onXScriptCreatedByTag[script.tag] = this.onEventScriptCreated; // for case 1
  1409.                 script.setBreakpoint(0);
  1410.  
  1411.                 fbs.nestedScriptStack.appendElement(script, false);  // for case 2
  1412.  
  1413.             }
  1414.             else if( reXUL.test(script.fileName) )
  1415.             {
  1416.                 fbs.pendingXULFileName = script.fileName;  // if these were different, we would already have called flushXUL()
  1417.                 fbs.nestedScriptStack.appendElement(script, false);
  1418.             }
  1419.             else
  1420.             {
  1421.                 fbs.nestedScriptStack.appendElement(script, false);
  1422.                 dispatch(scriptListeners,"onScriptCreated",[script, fileName, script.baseLineNumber]);
  1423.             }
  1424.         }
  1425.         catch(exc)
  1426.         {
  1427.             ERROR("onScriptCreated failed: "+exc);
  1428.             FBTrace.sysout("onScriptCreated failed: ", exc);
  1429.         }
  1430.     },
  1431.  
  1432.     flushXUL: function()
  1433.     {
  1434.         for ( var i = debuggers.length - 1; i >= 0; i--)
  1435.         {
  1436.             try
  1437.             {
  1438.                 var debuggr = debuggers[i];
  1439.                 if (debuggr.onXULScriptCreated)
  1440.                     debuggr.onXULScriptCreated(fbs.pendingXULFileName, fbs.nestedScriptStack.enumerate());
  1441.             }
  1442.             catch (exc)
  1443.             {
  1444.                 FBTrace.sysout("firebug-service flushXUL FAILS: ",exc);
  1445.             }
  1446.         }
  1447.         delete fbs.pendingXULFileName;
  1448.         fbs.clearNestedScripts();
  1449.     },
  1450.  
  1451.     createdScriptHasCaller: function()
  1452.     {
  1453.         var frame = Components.stack; // createdScriptHasCaller
  1454.  
  1455.         frame = frame.caller;         // onScriptCreated
  1456.         if (!frame) return frame;
  1457.  
  1458.         frame = frame.caller;         // hook apply
  1459.         if (!frame) return frame;
  1460.         frame = frame.caller;         // native interpret?
  1461.         if (!frame) return frame;
  1462.         frame = frame.caller;         // our creator ... or null if we are top level
  1463.         return frame;
  1464.     },
  1465.  
  1466.     onScriptDestroyed: function(script)
  1467.     {
  1468.         if (!fbs)
  1469.              return;
  1470.         if (script.tag in fbs.onXScriptCreatedByTag)
  1471.             delete  fbs.onXScriptCreatedByTag[script.tag];
  1472.  
  1473.         try
  1474.         {
  1475.             var fileName = script.fileName;
  1476.             if (isFilteredURL(fileName))
  1477.                 return;
  1478.             dispatch(scriptListeners,"onScriptDestroyed",[script]);
  1479.         }
  1480.         catch(exc)
  1481.         {
  1482.             ERROR("onScriptDestroyed failed: "+exc);
  1483.             FBTrace.sysout("onScriptDestroyed failed: ", exc);
  1484.         }
  1485.     },
  1486.  
  1487.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1488.  
  1489.     createFilter: function(pattern, pass)
  1490.     {
  1491.         var filter = {
  1492.                 globalObject: null,
  1493.                 flags: pass ? (jsdIFilter.FLAG_ENABLED | jsdIFilter.FLAG_PASS) : jsdIFilter.FLAG_ENABLED,
  1494.                 urlPattern: pattern,
  1495.                 startLine: 0,
  1496.                 endLine: 0
  1497.             };
  1498.         return filter;
  1499.     },
  1500.  
  1501.     setChromeBlockingFilters: function()
  1502.     {
  1503.         if (!fbs.isChromeBlocked)
  1504.         {
  1505.             jsd.appendFilter(this.noFilterHalter);  // must be first
  1506.             jsd.appendFilter(this.filterChrome);
  1507.             jsd.appendFilter(this.filterComponents);
  1508.             jsd.appendFilter(this.filterFirebugComponents);
  1509.             jsd.appendFilter(this.filterModules);
  1510.             jsd.appendFilter(this.filterStringBundle);
  1511.             jsd.appendFilter(this.filterPrettyPrint);
  1512.             jsd.appendFilter(this.filterWrapper);
  1513.  
  1514.             for (var i = 0; i < this.componentFilters.length; i++)
  1515.                 jsd.appendFilter(this.componentFilters[i]);
  1516.  
  1517.             fbs.isChromeBlocked = true;
  1518.  
  1519.         }
  1520.     },
  1521.  
  1522.     removeChromeBlockingFilters: function()
  1523.     {
  1524.         if (fbs.isChromeBlocked)
  1525.         {
  1526.             jsd.removeFilter(this.filterChrome);
  1527.             jsd.removeFilter(this.filterComponents);
  1528.             jsd.removeFilter(this.filterFirebugComponents);
  1529.             jsd.removeFilter(this.filterModules);
  1530.             jsd.removeFilter(this.filterStringBundle);
  1531.             jsd.removeFilter(this.filterPrettyPrint);
  1532.             jsd.removeFilter(this.filterWrapper);
  1533.             jsd.removeFilter(this.noFilterHalter);
  1534.             for (var i = 0; i < this.componentFilters.length; i++)
  1535.                 jsd.removeFilter(this.componentFilters[i]);
  1536.  
  1537.             fbs.isChromeBlocked = false;
  1538.         }
  1539.     },
  1540.  
  1541.     createChromeBlockingFilters: function() // call after components are loaded.
  1542.     {
  1543.         try
  1544.         {
  1545.             this.filterModules = this.createFilter("*/firefox/modules/*");
  1546.             this.filterComponents = this.createFilter("*/firefox/components/*");
  1547.             this.filterFirebugComponents = this.createFilter("*/components/firebug-*");
  1548.             this.filterStringBundle = this.createFilter("XStringBundle");
  1549.             this.filterChrome = this.createFilter("chrome://*");
  1550.             this.filterPrettyPrint = this.createFilter("x-jsd:ppbuffer*");
  1551.             this.filterWrapper = this.createFilter("XPCSafeJSObjectWrapper.cpp");
  1552.             this.noFilterHalter = this.createFilter("chrome://firebug/content/debuggerHalter.js", true);
  1553.  
  1554.             // jsdIFilter does not allow full regexp matching.
  1555.             // So to filter components, we filter their directory names, which we obtain by looking for
  1556.             // scripts that match regexps
  1557.  
  1558.             var componentsUnfound = [];
  1559.             for( var i = 0; i < COMPONENTS_FILTERS.length; ++i )
  1560.             {
  1561.                 componentsUnfound.push(COMPONENTS_FILTERS[i]);
  1562.             }
  1563.  
  1564.             this.componentFilters = [];
  1565.  
  1566.             jsd.enumerateScripts( {
  1567.                 enumerateScript: function(script) {
  1568.                     var fileName = script.fileName;
  1569.                 for( var i = 0; i < componentsUnfound.length; ++i )
  1570.                     {
  1571.                         if ( componentsUnfound[i].test(fileName) )
  1572.                         {
  1573.                             var match = componentsUnfound[i].exec(fileName);
  1574.                             fbs.componentFilters.push(fbs.createFilter(match[1]));
  1575.                             componentsUnfound.splice(i, 1);
  1576.                             return;
  1577.                         }
  1578.                     }
  1579.                 }
  1580.             });
  1581.         } catch (exc) {
  1582.             FBTrace.sysout("createChromeblockingFilters fails >>>>>>>>>>>>>>>>> "+exc, exc);
  1583.         }
  1584.  
  1585.     },
  1586.  
  1587.     traceFilters: function(from)
  1588.     {
  1589.         FBTrace.sysout("fbs.traceFilters from "+from);
  1590.         jsd.enumerateFilters({ enumerateFilter: function(filter)
  1591.             {
  1592.                 FBTrace.sysout("jsdIFilter "+filter.urlPattern, filter);
  1593.             }});
  1594.     },
  1595.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1596.  
  1597.     getJSContexts: function()
  1598.     {
  1599.         var enumeratedContexts = [];
  1600.         jsd.enumerateContexts( {enumerateContext: function(jscontext)
  1601.         {
  1602.                 try
  1603.                 {
  1604.                     if (!jscontext.isValid)
  1605.                         return;
  1606.  
  1607.                     var wrappedGlobal = jscontext.globalObject;
  1608.                     if (!wrappedGlobal)
  1609.                         return;
  1610.  
  1611.                     var unwrappedGlobal = wrappedGlobal.getWrappedValue();
  1612.                     if (!unwrappedGlobal)
  1613.                         return;
  1614.  
  1615.                     var global = new XPCNativeWrapper(unwrappedGlobal);
  1616.  
  1617.                     if (global)
  1618.                     {
  1619.                         var document = global.document;
  1620.                         if (document)
  1621.                         {
  1622.                         }
  1623.                         else
  1624.                         {
  1625.                             return; // skip these
  1626.                         }
  1627.                     }
  1628.                     else
  1629.                     {
  1630.                         return; // skip this
  1631.                     }
  1632.  
  1633.                     enumeratedContexts.push(jscontext);
  1634.                 }
  1635.                 catch(e)
  1636.                 {
  1637.                     FBTrace.sysout("jscontext dump FAILED "+e, e);
  1638.                 }
  1639.  
  1640.         }});
  1641.         return enumeratedContexts;
  1642.     },
  1643.  
  1644.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1645.  
  1646.     findDebugger: function(frame)
  1647.     {
  1648.         if (debuggers.length < 1)
  1649.             return;
  1650.  
  1651.         var checkFrame = frame;
  1652.         while (checkFrame) // We may stop in a component, but want the callers Window
  1653.         {
  1654.             var frameScopeRoot = getFrameScopeRoot(checkFrame);
  1655.             if (frameScopeRoot)
  1656.                 break;
  1657.  
  1658.             checkFrame = checkFrame.callingFrame;
  1659.         }
  1660.  
  1661.         if (!checkFrame && FBTrace.DBG_FBS_FINDDEBUGGER)
  1662.             FBTrace.sysout("fbs.findDebugger fell thru bottom of stack", frame);
  1663.  
  1664.         // frameScopeRoot should be the top window for the scope of the frame function
  1665.         // or null
  1666.         fbs.last_debuggr = fbs.askDebuggersForSupport(frameScopeRoot, frame);
  1667.         if (fbs.last_debuggr)
  1668.              return fbs.last_debuggr;
  1669.         else
  1670.             return null;
  1671.     },
  1672.  
  1673.     isChromebug: function(global)
  1674.     {
  1675.         // TODO this is a kludge: isFilteredURL stops users from seeing firebug but chromebug has to disable the filter
  1676.  
  1677.         var location = fbs.getLocationSafe(global);
  1678.         if (location)
  1679.         {
  1680.             if (location.indexOf("chrome://chromebug/") >= 0 || location.indexOf("chrome://fb4cb/") >= 0)
  1681.                 return true;
  1682.         }
  1683.         return false;
  1684.     },
  1685.  
  1686.     getLocationSafe: function(global)
  1687.     {
  1688.         try
  1689.         {
  1690.             if (global && global.location)  // then we have a window, it will be an nsIDOMWindow, right?
  1691.                 return global.location.toString();
  1692.             else if (global && global.tag)
  1693.                 return "global_tag_"+global.tag;
  1694.         }
  1695.         catch (exc)
  1696.         {
  1697.             // FF3 gives (NS_ERROR_INVALID_POINTER) [nsIDOMLocation.toString]
  1698.         }
  1699.         return null;
  1700.     },
  1701.  
  1702.     askDebuggersForSupport: function(global, frame)
  1703.     {
  1704.         if (global && fbs.isChromebug(global))
  1705.             return false;
  1706.  
  1707.         for ( var i = debuggers.length - 1; i >= 0; i--)
  1708.         {
  1709.             try
  1710.             {
  1711.                 var debuggr = debuggers[i];
  1712.                 if (debuggr.supportsGlobal(global, frame))
  1713.                 {
  1714.                     if (!debuggr.breakContext)
  1715.                         FBTrace.sysout("Debugger with no breakContext:",debuggr.supportsGlobal);
  1716.                     return debuggr;
  1717.                 }
  1718.             }
  1719.             catch (exc)
  1720.             {
  1721.                 FBTrace.sysout("firebug-service askDebuggersForSupport FAILS: "+exc,exc);
  1722.             }
  1723.         }
  1724.         return null;
  1725.     },
  1726.  
  1727.     dumpIValue: function(value)
  1728.     {
  1729.         var listValue = {value: null}, lengthValue = {value: 0};
  1730.         value.getProperties(listValue, lengthValue);
  1731.         for (var i = 0; i < lengthValue.value; ++i)
  1732.         {
  1733.             var prop = listValue.value[i];
  1734.             try {
  1735.                 var name = unwrapIValue(prop.name);
  1736.                 FBTrace.sysout(i+"]"+name+"="+unwrapIValue(prop.value));
  1737.             }
  1738.             catch (e)
  1739.             {
  1740.                 FBTrace.sysout(i+"]"+e);
  1741.             }
  1742.         }
  1743.     },
  1744.  
  1745.     reFindDebugger: function(frame, debuggr)
  1746.     {
  1747.         var frameScopeRoot = getFrameScopeRoot(frame);
  1748.         if (frameScopeRoot && debuggr.supportsGlobal(frameScopeRoot, frame)) return debuggr;
  1749.  
  1750.         return null;
  1751.     },
  1752.  
  1753.     discardRecursionFrames: function(frame)
  1754.     {
  1755.         var i = 0;
  1756.         var rest = 0;
  1757.         var mark = frame;  // a in abcabcabcdef
  1758.         var point = frame;
  1759.         while (point = point.callingFrame)
  1760.         {
  1761.             i++;
  1762.             if (point.script.tag == mark.script.tag) // then we found a repeating caller abcabcdef
  1763.             {
  1764.                 mark = point;
  1765.                 rest = i;
  1766.             }
  1767.         }
  1768.         // here point is null and mark is the last repeater, abcdef
  1769.         return mark;
  1770.     },
  1771.  
  1772.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1773.     // jsd breakpoints are on a PC in a jsdIScript
  1774.     // Users breakpoint on a line of source
  1775.     // Because test.js can be included multiple times, the URL+line number from the UI is not unique.
  1776.     // sourcefile.href != script.fileName, generally script.fileName cannot be used.
  1777.     // If the source is compiled, then we have zero, one, or more jsdIScripts on a line.
  1778.     //    If zero, cannot break at that line
  1779.     //    If one, set a jsd breakpoint
  1780.     //    If more than one, set jsd breakpoint on each script
  1781.     // Else we know that the source will be compiled before it is run.
  1782.     //    Save the sourceFile.href+line and set the jsd breakpoint when we compile
  1783.     //    Venkman called these "future" breakpoints
  1784.     //    We cannot prevent future breakpoints on lines that have no script.  Break onCreate with error?
  1785.  
  1786.     addBreakpoint: function(type, sourceFile, lineNo, props, debuggr)
  1787.     {
  1788.         var url = sourceFile.href;
  1789.         var bp = this.findBreakpoint(url, lineNo);
  1790.         if (bp && bp.type & type)
  1791.             return null;
  1792.  
  1793.         if (bp)
  1794.         {
  1795.             bp.type |= type;
  1796.  
  1797.             if (debuggr)
  1798.                 bp.debugger = debuggr;
  1799.             else
  1800.             {
  1801.             }
  1802.         }
  1803.         else
  1804.         {
  1805.             bp = this.recordBreakpoint(type, url, lineNo, debuggr, props);
  1806.             fbs.setJSDBreakpoint(sourceFile, bp);
  1807.         }
  1808.         return bp;
  1809.     },
  1810.  
  1811.     recordBreakpoint: function(type, url, lineNo, debuggr, props)
  1812.     {
  1813.         var urlBreakpoints = breakpoints[url];
  1814.         if (!urlBreakpoints)
  1815.             breakpoints[url] = urlBreakpoints = [];
  1816.  
  1817.         var bp = {type: type, href: url, lineNo: lineNo, disabled: 0,
  1818.             debugger: debuggr,
  1819.             condition: "", onTrue: true, hitCount: -1, hit: 0};
  1820.         if (props)
  1821.         {
  1822.             bp.condition = props.condition;
  1823.             bp.onTrue = props.onTrue;
  1824.             bp.hitCount = props.hitCount;
  1825.             if (bp.condition || bp.hitCount > 0)
  1826.                 ++conditionCount;
  1827.             if(props.disabled)
  1828.             {
  1829.                 bp.disabled |= BP_NORMAL;
  1830.                 ++disabledCount;
  1831.             }
  1832.         }
  1833.         urlBreakpoints.push(bp);
  1834.         ++breakpointCount;
  1835.         return bp;
  1836.     },
  1837.  
  1838.     removeBreakpoint: function(type, url, lineNo)
  1839.     {
  1840.         var urlBreakpoints = breakpoints[url];
  1841.         if (!urlBreakpoints)
  1842.             return false;
  1843.  
  1844.         for (var i = 0; i < urlBreakpoints.length; ++i)
  1845.         {
  1846.             var bp = urlBreakpoints[i];
  1847.             if (bp.lineNo == lineNo)
  1848.             {
  1849.                 bp.type &= ~type;
  1850.                 if (!bp.type)
  1851.                 {
  1852.                     if (bp.scriptsWithBreakpoint)
  1853.                     {
  1854.                         for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  1855.                         {
  1856.                             var script = bp.scriptsWithBreakpoint[j];
  1857.                             if (script && script.isValid)
  1858.                             {
  1859.                                 try
  1860.                                 {
  1861.                                     script.clearBreakpoint(bp.pc[j]);
  1862.                                 }
  1863.                                 catch (exc)
  1864.                                 {
  1865.                                     FBTrace.sysout("Firebug service failed to remove breakpoint in "+script.tag+" at lineNo="+lineNo+" pcmap:"+bp.pcmap);
  1866.                                 }
  1867.                             }
  1868.                         }
  1869.                     }
  1870.                     // else this was a future breakpoint that never hit or a script that was GCed
  1871.  
  1872.                     urlBreakpoints.splice(i, 1);
  1873.                     --breakpointCount;
  1874.  
  1875.                     if (bp.disabled)
  1876.                         --disabledCount;
  1877.  
  1878.                     if (bp.condition || bp.hitCount > 0)
  1879.                     {
  1880.                         --conditionCount;
  1881.                     }
  1882.  
  1883.  
  1884.                     if (!urlBreakpoints.length)
  1885.                         delete breakpoints[url];
  1886.  
  1887.                 }
  1888.                 return bp;
  1889.             }
  1890.         }
  1891.  
  1892.         return false;
  1893.     },
  1894.  
  1895.     findBreakpoint: function(url, lineNo)
  1896.     {
  1897.         var urlBreakpoints = breakpoints[url];
  1898.         if (urlBreakpoints)
  1899.         {
  1900.             for (var i = 0; i < urlBreakpoints.length; ++i)
  1901.             {
  1902.                 var bp = urlBreakpoints[i];
  1903.                 if (bp.lineNo == lineNo)
  1904.                     return bp;
  1905.             }
  1906.         }
  1907.         return null;
  1908.     },
  1909.  
  1910.     // When we are called, scripts have been compiled so all relevant breakpoints are not "future"
  1911.     findBreakpointByScript: function(script, pc)
  1912.     {
  1913.         for (var url in breakpoints)
  1914.         {
  1915.             var urlBreakpoints = breakpoints[url];
  1916.             if (urlBreakpoints)
  1917.             {
  1918.                 for (var i = 0; i < urlBreakpoints.length; ++i)
  1919.                 {
  1920.                     var bp = urlBreakpoints[i];
  1921.                     if (bp.scriptsWithBreakpoint)
  1922.                     {
  1923.                         for (var j = 0; j < bp.scriptsWithBreakpoint.length; j++)
  1924.                         {
  1925.                             if ( bp.scriptsWithBreakpoint[j] && (bp.scriptsWithBreakpoint[j].tag == script.tag) && (bp.pc[j] == pc) )
  1926.                                 return bp;
  1927.                         }
  1928.                     }
  1929.                 }
  1930.             }
  1931.         }
  1932.  
  1933.         return null;
  1934.     },
  1935.  
  1936.     resetBreakpoints: function(sourceFile, lastLineNumber) // the sourcefile has just been created after compile
  1937.     {
  1938.         // If the new script is replacing an old script with a breakpoint still
  1939.         var url = sourceFile.href;
  1940.         var urlBreakpoints = breakpoints[url];
  1941.         if (urlBreakpoints)
  1942.         {
  1943.             for (var i = 0; i < urlBreakpoints.length; ++i)
  1944.             {
  1945.                 var bp = urlBreakpoints[i];
  1946.                 fbs.setJSDBreakpoint(sourceFile, bp);
  1947.                 if (lastLineNumber && !bp.jsdLine && !(bp.disabled & BP_NORMAL) && (bp.lineNo < lastLineNumber))
  1948.                 {
  1949.                      fbs.disableBreakpoint(url, bp.lineNo);
  1950.                 }
  1951.             }
  1952.         }
  1953.         else
  1954.         {
  1955.         }
  1956.     },
  1957.  
  1958.     setJSDBreakpoint: function(sourceFile, bp)
  1959.     {
  1960.         var scripts = sourceFile.getScriptsAtLineNumber(bp.lineNo);
  1961.         if (!scripts)
  1962.         {
  1963.              if (!sourceFile.outerScript || !sourceFile.outerScript.isValid)
  1964.              {
  1965.                 return;
  1966.              }
  1967.              scripts = [sourceFile.outerScript];
  1968.         }
  1969.  
  1970.         bp.scriptsWithBreakpoint = [];
  1971.         bp.pc = [];
  1972.         for (var i = 0; i < scripts.length; i++)
  1973.         {
  1974.             var script = scripts[i];
  1975.             if (!script.isValid)
  1976.             {
  1977.                 continue;
  1978.             }
  1979.  
  1980.             var pcmap = sourceFile.pcmap_type;
  1981.             if (!pcmap)
  1982.             {
  1983.                 pcmap = PCMAP_SOURCETEXT;
  1984.             }
  1985.             // we subtraced this offset when we showed the user so lineNo is a user line number; now we need to talk
  1986.             // to jsd its line world
  1987.             var jsdLine = bp.lineNo + sourceFile.getBaseLineOffset();
  1988.             // test script.isLineExecutable(jsdLineNo, pcmap) ??
  1989.  
  1990.             var isExecutable = false;
  1991.             try {
  1992.                  isExecutable = script.isLineExecutable(jsdLine, pcmap);
  1993.             } catch(e) {
  1994.                 // guess not then...
  1995.             }
  1996.             if (isExecutable)
  1997.             {
  1998.                 var pc = script.lineToPc(jsdLine, pcmap);
  1999.                 var pcToLine = script.pcToLine(pc, pcmap);  // avoid calling this unless we have to...
  2000.  
  2001.                 if (pcToLine == jsdLine)
  2002.                 {
  2003.                     script.setBreakpoint(pc);
  2004.  
  2005.                     bp.scriptsWithBreakpoint.push(script);
  2006.                     bp.pc.push(pc);
  2007.                     bp.pcmap = pcmap;
  2008.                     bp.jsdLine = jsdLine;
  2009.  
  2010.                     if (pc == 0)  // signal the breakpoint handler to break for user
  2011.                         sourceFile.breakOnZero = script.tag;
  2012.  
  2013.                 }
  2014.                 else
  2015.                 {
  2016.                 }
  2017.             }
  2018.             else
  2019.             {
  2020.             }
  2021.          }
  2022.     },
  2023.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2024.  
  2025.     breakIntoDebugger: function(debuggr, frame, type)
  2026.     {
  2027.         // Before we break, clear information about previous stepping session
  2028.         this.stopStepping();
  2029.  
  2030.         // Break into the debugger - execution will stop here until the user resumes
  2031.         var returned;
  2032.         try
  2033.         {
  2034.             returned = debuggr.onBreak(frame, type);
  2035.         }
  2036.         catch (exc)
  2037.         {
  2038.             ERROR(exc);
  2039.             returned = RETURN_CONTINUE;
  2040.         }
  2041.  
  2042.         // Execution resumes now. Check if the user requested stepping and if so
  2043.         // install the necessary hooks
  2044.         hookFrameCount = countFrames(frame);
  2045.         this.startStepping();
  2046.         return returned;
  2047.     },
  2048.  
  2049.     needToBreakForError: function(fileName, lineNo)
  2050.     {
  2051.         return breakOnNextError =
  2052.             this.breakOnErrors || this.findErrorBreakpoint(this.normalizeURL(fileName), lineNo) != -1;
  2053.     },
  2054.  
  2055.     startStepping: function()
  2056.     {
  2057.         if (!stepMode && !runningUntil)
  2058.             return;
  2059.  
  2060.         this.hookFunctions();
  2061.  
  2062.         if (stepMode == STEP_OVER || stepMode == STEP_INTO)
  2063.             this.hookInterrupts();
  2064.     },
  2065.  
  2066.     stopStepping: function()
  2067.     {
  2068.         stepMode = 0;
  2069.         stepFrame = null;
  2070.         stepFrameCount = 0;
  2071.         stepFrameLineId = null;
  2072.  
  2073.         if (runningUntil)
  2074.         {
  2075.             this.removeBreakpoint(BP_UNTIL, runningUntil.href, runningUntil.lineNo);
  2076.             runningUntil = null;
  2077.         }
  2078.  
  2079.         jsd.interruptHook = null;
  2080.         jsd.functionHook = null;
  2081.     },
  2082.  
  2083.     /*
  2084.      * Returns a string describing the step mode or null for not stepping.
  2085.      */
  2086.     getStepMode: function()
  2087.     {
  2088.         return getStepName(stepMode);
  2089.     },
  2090.  
  2091.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2092.  
  2093.     hookFunctions: function()
  2094.     {
  2095.         function functionHook(frame, type)
  2096.         {
  2097.             switch (type)
  2098.             {
  2099.                 case TYPE_TOPLEVEL_START: // fall through
  2100.                 case TYPE_FUNCTION_CALL:
  2101.                 {
  2102.                     ++hookFrameCount;
  2103.  
  2104.                     if (stepMode == STEP_OVER)
  2105.                         jsd.interruptHook = null;
  2106.  
  2107.                     if (stepMode == STEP_INTO)  // normally step into will break in the interrupt handler, but not in event handlers.
  2108.                     {
  2109.                         fbs.stopStepping();
  2110.                         stepMode = STEP_SUSPEND; // break on next
  2111.                         fbs.hookInterrupts();
  2112.                     }
  2113.  
  2114.                     break;
  2115.                 }
  2116.                 case TYPE_TOPLEVEL_END: // fall through
  2117.                 case TYPE_FUNCTION_RETURN:
  2118.                 {
  2119.                     --hookFrameCount;
  2120.  
  2121.                     if (hookFrameCount == 0) {  // stack empty
  2122.                         if ( (stepMode == STEP_INTO) || (stepMode == STEP_OVER) ) {
  2123.                             fbs.stopStepping();
  2124.                             stepMode = STEP_SUSPEND; // break on next
  2125.                             fbs.hookInterrupts();
  2126.                         }
  2127.                         else
  2128.                         {
  2129.                             fbs.stopStepping();
  2130.                         }
  2131.                     }
  2132.                     else if (stepMode == STEP_OVER)
  2133.                     {
  2134.                         if (hookFrameCount <= stepFrameCount)
  2135.                             fbs.hookInterrupts();
  2136.                     }
  2137.                     else if (stepMode == STEP_OUT)
  2138.                     {
  2139.                         if (hookFrameCount < stepFrameCount)
  2140.                             fbs.hookInterrupts();
  2141.                     }
  2142.  
  2143.                     break;
  2144.                 }
  2145.             }
  2146.         }
  2147.  
  2148.         jsd.functionHook = { onCall: functionHook };
  2149.     },
  2150.  
  2151.     hookInterrupts: function()
  2152.     {
  2153.         function interruptHook(frame, type, rv)
  2154.         {
  2155.             /*if ( isFilteredURL(frame.script.fileName) )  // it does not seem feasible to use jsdIFilter-ing TODO try again
  2156.             {
  2157.                 return RETURN_CONTINUE;
  2158.             }
  2159.              */
  2160.             // Sometimes the same line will have multiple interrupts, so check
  2161.             // a unique id for the line and don't break until it changes
  2162.             var frameLineId = hookFrameCount + frame.script.fileName + frame.line;
  2163.             if (frameLineId != stepFrameLineId)
  2164.                 return fbs.onBreak(frame, type, rv);
  2165.             else
  2166.                 return RETURN_CONTINUE;
  2167.         }
  2168.  
  2169.         jsd.interruptHook = { onExecute: interruptHook };
  2170.     },
  2171.  
  2172.     hookScripts: function()
  2173.     {
  2174.         jsd.scriptHook = {
  2175.             onScriptCreated: hook(this.onScriptCreated),
  2176.             onScriptDestroyed: hook(this.onScriptDestroyed)
  2177.         };
  2178.         if (fbs.filterSystemURLs)
  2179.             fbs.setChromeBlockingFilters();
  2180.  
  2181.         jsd.debuggerHook = { onExecute: hook(this.onDebugger, RETURN_CONTINUE) };
  2182.         jsd.debugHook = { onExecute: hook(this.onDebug, RETURN_CONTINUE) };
  2183.         jsd.breakpointHook = { onExecute: hook(this.onBreakpoint, RETURN_CONTINUE) };
  2184.         jsd.throwHook = { onExecute: hook(this.onThrow, RETURN_CONTINUE_THROW) };
  2185.         jsd.errorHook = { onError: hook(this.onError, true) };
  2186.     },
  2187.  
  2188.     unhookScripts: function()
  2189.     {
  2190.         jsd.scriptHook = null;
  2191.         fbs.removeChromeBlockingFilters();
  2192.  
  2193.     },
  2194.  
  2195.     hookCalls: function(callBack, unhookAtBottom)
  2196.     {
  2197.         var contextCached = null;
  2198.  
  2199.         function callHook(frame, type)
  2200.         {
  2201.             switch (type)
  2202.             {
  2203.                 case TYPE_FUNCTION_CALL:
  2204.                 {
  2205.                     ++hookFrameCount;
  2206.  
  2207.                     contextCached = callBack(contextCached, frame, hookFrameCount, true);
  2208.  
  2209.                     break;
  2210.                 }
  2211.                 case TYPE_FUNCTION_RETURN:
  2212.                 {
  2213.                     if(hookFrameCount <= 0)  // ignore returns until we have started back in
  2214.                         return;
  2215.  
  2216.                     --hookFrameCount;
  2217.                     if (unhookAtBottom && hookFrameCount == 0) {  // stack empty
  2218.                        jsd.functionHook = null;
  2219.                     }
  2220.  
  2221.                     contextCached = callBack(contextCached, frame, hookFrameCount, false);
  2222.  
  2223.                     break;
  2224.                 }
  2225.             }
  2226.         }
  2227.  
  2228.         if (jsd.functionHook)
  2229.         {
  2230.             return;
  2231.         }
  2232.  
  2233.         hookFrameCount = 0;
  2234.         jsd.functionHook = { onCall: callHook };
  2235.     },
  2236.  
  2237.     getJSD: function()
  2238.     {
  2239.         return jsd; // for debugging fbs
  2240.     },
  2241.  
  2242.     dumpFileTrack: function(moreFiles)
  2243.     {
  2244.         if (moreFiles)
  2245.             trackFiles.merge(moreFiles);
  2246.         trackFiles.dump();
  2247.     },
  2248.  
  2249. };
  2250.  
  2251. function getStepName(mode)
  2252. {
  2253.     if (mode==STEP_OVER) return "STEP_OVER";
  2254.     if (mode==STEP_INTO) return "STEP_INTO";
  2255.     if (mode==STEP_OUT) return "STEP_OUT";
  2256.     if (mode==STEP_SUSPEND) return "STEP_SUSPEND";
  2257.     else return "(not a step mode)";
  2258. }
  2259.  
  2260. // ************************************************************************************************
  2261.  
  2262. var FirebugFactory =
  2263. {
  2264.     createInstance: function (outer, iid)
  2265.     {
  2266.         if (outer != null)
  2267.             throw NS_ERROR_NO_AGGREGATION;
  2268.  
  2269.         FirebugFactory.initializeService();
  2270.         return (new FirebugService()).QueryInterface(iid);
  2271.     },
  2272.     initializeService: function()
  2273.     {
  2274.         if (!prefs)
  2275.            prefs = PrefService.getService(nsIPrefBranch2);
  2276.  
  2277.         var filterSystemURLs =  prefs.getBoolPref("extensions.firebug.service.filterSystemURLs");
  2278.         if (filterSystemURLs)  // do not turn jsd on unless we want to see chrome
  2279.             return;
  2280.  
  2281.         try
  2282.         {
  2283.             var jsd = DebuggerService.getService(jsdIDebuggerService);
  2284.             jsd.initAtStartup = false;
  2285.         }
  2286.         catch (exc)
  2287.         {
  2288.         }
  2289.     }
  2290. };
  2291.  
  2292. // ************************************************************************************************
  2293.  
  2294. var FirebugModule =
  2295. {
  2296.     registerSelf: function (compMgr, fileSpec, location, type)
  2297.     {
  2298.         compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
  2299.         compMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, fileSpec, location, type);
  2300.     },
  2301.  
  2302.     unregisterSelf: function(compMgr, fileSpec, location)
  2303.     {
  2304.         compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
  2305.         compMgr.unregisterFactoryLocation(CLASS_ID, location);
  2306.     },
  2307.  
  2308.     getClassObject: function (compMgr, cid, iid)
  2309.     {
  2310.         if (!iid.equals(nsIFactory))
  2311.             throw NS_ERROR_NOT_IMPLEMENTED;
  2312.  
  2313.         if (cid.equals(CLASS_ID))
  2314.             return FirebugFactory;
  2315.  
  2316.         throw NS_ERROR_NO_INTERFACE;
  2317.     },
  2318.  
  2319.     canUnload: function(compMgr)
  2320.     {
  2321.         return true;
  2322.     }
  2323. };
  2324.  
  2325. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2326.  
  2327. function NSGetModule(compMgr, fileSpec)
  2328. {
  2329.     return FirebugModule;
  2330. }
  2331.  
  2332. // ************************************************************************************************
  2333. // Local Helpers
  2334.  
  2335. // called by enumerateScripts, onThrow, onDebug, onScriptCreated/Destroyed.
  2336. function isFilteredURL(rawJSD_script_filename)
  2337. {
  2338.     if (!rawJSD_script_filename)
  2339.         return true;
  2340.     if (fbs.filterConsoleInjections)
  2341.         return true;
  2342.     if (rawJSD_script_filename[0] == 'h')
  2343.         return false;
  2344.     if (rawJSD_script_filename == "XPCSafeJSObjectWrapper.cpp")
  2345.         return true;
  2346.     if (fbs.filterSystemURLs)
  2347.         return systemURLStem(rawJSD_script_filename);
  2348.     for (var i = 0; i < fbs.alwayFilterURLsStarting.length; i++)
  2349.         if (rawJSD_script_filename.indexOf(fbs.alwayFilterURLsStarting[i]) != -1)
  2350.             return true;
  2351.     return false;
  2352. }
  2353.  
  2354. function systemURLStem(rawJSD_script_filename)
  2355. {
  2356.     if (this.url_class)  // attempt to optimize stream of similar urls
  2357.     {
  2358.         if ( rawJSD_script_filename.substr(0,this.url_class.length) == this.url_class )
  2359.             return this.url_class;
  2360.     }
  2361.     this.url_class = deepSystemURLStem(rawJSD_script_filename);
  2362.     return this.url_class;
  2363. }
  2364.  
  2365. function deepSystemURLStem(rawJSD_script_filename)
  2366. {
  2367.     for( var i = 0; i < urlFilters.length; ++i )
  2368.     {
  2369.         var filter = urlFilters[i];
  2370.         if ( rawJSD_script_filename.substr(0,filter.length) == filter )
  2371.             return filter;
  2372.     }
  2373.     for( var i = 0; i < COMPONENTS_FILTERS.length; ++i )
  2374.     {
  2375.         if ( COMPONENTS_FILTERS[i].test(rawJSD_script_filename) )
  2376.         {
  2377.             var match = COMPONENTS_FILTERS[i].exec(rawJSD_script_filename);
  2378.             urlFilters.push(match[1]);  // cache this for future calls
  2379.             return match[1];
  2380.         }
  2381.     }
  2382.     return false;
  2383. }
  2384.  
  2385. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2386.  
  2387. function dispatch(listeners, name, args)
  2388. {
  2389.     var totalListeners = listeners.length;
  2390.     for (var i = 0; i < totalListeners; ++i)
  2391.     {
  2392.         var listener = listeners[i];
  2393.         if ( listener.hasOwnProperty(name) )
  2394.             listener[name].apply(listener, args);
  2395.     }
  2396.     //if (FBTrace.DBG_FBS_ERRORS)
  2397.     //    FBTrace.sysout("fbs.dispatch "+name+" to "+listeners.length+" listeners");
  2398. }
  2399.  
  2400. function hook(fn, rv)
  2401. {
  2402.     return function()
  2403.     {
  2404.         try
  2405.         {
  2406.             return fn.apply(fbs, arguments);
  2407.         }
  2408.         catch (exc)
  2409.         {
  2410.             var msg =  "Error in hook: "+ exc +" fn=\n"+fn+"\n stack=\n";
  2411.             for (var frame = Components.stack; frame; frame = frame.caller)
  2412.                 msg += frame.filename + "@" + frame.line + ";\n";
  2413.                ERROR(msg);
  2414.             return rv;
  2415.         }
  2416.     }
  2417. }
  2418. var lastWindowScope = null;
  2419. function getFrameScopeRoot(frame)  // walk script scope chain to bottom, convert to Window if possible
  2420. {
  2421.     var scope = frame.scope;
  2422.     if (scope)
  2423.     {
  2424.         while(scope.jsParent)
  2425.             scope = scope.jsParent;
  2426.  
  2427.         // These are just determined by trial and error.
  2428.         if (scope.jsClassName == "Window" || scope.jsClassName == "ChromeWindow" || scope.jsClassName == "ModalContentWindow")
  2429.         {
  2430.             lastWindowScope = new XPCNativeWrapper(scope.getWrappedValue());
  2431.             return  lastWindowScope;
  2432.         }
  2433.  
  2434.         if (scope.jsClassName == "DedicatedWorkerGlobalScope")
  2435.         {
  2436.             //var workerScope = new XPCNativeWrapper(scope.getWrappedValue());
  2437.  
  2438.             //if (FBTrace.DBG_FBS_FINDDEBUGGER)
  2439.             //        FBTrace.sysout("fbs.getFrameScopeRoot found WorkerGlobalScope: "+scope.jsClassName, workerScope);
  2440.             // https://bugzilla.mozilla.org/show_bug.cgi?id=507930 if (FBTrace.DBG_FBS_FINDDEBUGGER)
  2441.             //        FBTrace.sysout("fbs.getFrameScopeRoot found WorkerGlobalScope.location: "+workerScope.location, workerScope.location);
  2442.             return null; // https://bugzilla.mozilla.org/show_bug.cgi?id=507783
  2443.         }
  2444.  
  2445.         if (scope.jsClassName == "Sandbox")
  2446.         {
  2447.             var proto = scope.jsPrototype;
  2448.             if (proto.jsClassName == "XPCNativeWrapper")
  2449.                 proto = proto.jsParent;
  2450.             if (proto.jsClassName == "Window")
  2451.                 return new XPCNativeWrapper(proto.getWrappedValue());
  2452.         }
  2453.  
  2454.         try
  2455.         {
  2456.              return new XPCNativeWrapper(scope.getWrappedValue());
  2457.         }
  2458.         catch(exc)
  2459.         {
  2460.         }
  2461.     }
  2462.     else
  2463.         return null;
  2464. }
  2465.  
  2466. function getFrameGlobal(frame)
  2467. {
  2468.     var jscontext = frame.executionContext;
  2469.     if (!jscontext)
  2470.     {
  2471.         return getFrameWindow(frame);
  2472.     }
  2473.     try
  2474.     {
  2475.         var frameGlobal = new XPCNativeWrapper(jscontext.globalObject.getWrappedValue());
  2476.     }
  2477.     catch(exc)
  2478.     {
  2479.     }
  2480.     if (frameGlobal)
  2481.             return frameGlobal;
  2482.     else
  2483.     {
  2484.         return getFrameWindow(frame);
  2485.     }
  2486. }
  2487.  
  2488. function getFrameWindow(frame)
  2489. {
  2490.     if (debuggers.length < 1)  // too early, frame.eval will crash FF2
  2491.             return;
  2492.     try
  2493.     {
  2494.         var result = {};
  2495.         frame.eval("window", "", 1, result);
  2496.         var win = unwrapIValue(result.value);
  2497.         if (win instanceof Ci.nsIDOMWindow)
  2498.             return getRootWindow(win);
  2499.         else
  2500.             return getFrameScopeRoot(frame);
  2501.     }
  2502.     catch (exc)
  2503.     {
  2504.         return null;
  2505.     }
  2506. }
  2507.  
  2508. function getRootWindow(win)
  2509. {
  2510.     for (; win; win = win.parent)
  2511.     {
  2512.         if (!win.parent || win == win.parent || !(win.parent instanceof Window) )
  2513.             return win;
  2514.     }
  2515.     return null;
  2516. }
  2517.  
  2518. function countFrames(frame)
  2519. {
  2520.     var frameCount = 0;
  2521.     try
  2522.     {
  2523.         for (; frame; frame = frame.callingFrame)
  2524.             ++frameCount;
  2525.     }
  2526.     catch(exc)
  2527.     {
  2528.  
  2529.     }
  2530.  
  2531.     return frameCount;
  2532. }
  2533.  
  2534. function testBreakpoint(frame, bp)
  2535. {
  2536.     if ( bp.condition && bp.condition != "" )
  2537.     {
  2538.         var result = {};
  2539.         frame.scope.refresh();
  2540.         if (frame.eval(bp.condition, "", 1, result))
  2541.         {
  2542.             if (bp.onTrue)
  2543.             {
  2544.                 if (!result.value.booleanValue)
  2545.                     return false;
  2546.             } else
  2547.             {
  2548.                 var value = unwrapIValue(result.value);
  2549.                 if (typeof bp.lastValue == "undefined")
  2550.                 {
  2551.                     bp.lastValue = value;
  2552.                     return false;
  2553.                 } else
  2554.                 {
  2555.                     if (bp.lastValue == value)
  2556.                         return false;
  2557.                     bp.lastValue = value;
  2558.                 }
  2559.             }
  2560.         }
  2561.     }
  2562.     ++bp.hit;
  2563.     if ( bp.hitCount > 0 )
  2564.     {
  2565.         if ( bp.hit < bp.hitCount )
  2566.             return false;
  2567.     }
  2568.     return true;
  2569. }
  2570.  
  2571. function remove(list, item)
  2572. {
  2573.     var index = list.indexOf(item);
  2574.     if (index != -1)
  2575.         list.splice(index, 1);
  2576. }
  2577.  
  2578. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2579.  
  2580. var FirebugPrefsObserver =
  2581. {
  2582.     syncFilter: function()
  2583.     {
  2584.         var filter = fbs.scriptsFilter;
  2585.         fbs.showEvents = (filter == "all" || filter == "events");
  2586.         fbs.showEvals = (filter == "all" || filter == "evals");
  2587.     }
  2588. };
  2589.  
  2590. var QuitApplicationGrantedObserver =
  2591. {
  2592.     observe: function(subject, topic, data)
  2593.     {
  2594.     }
  2595. };
  2596. var QuitApplicationRequestedObserver =
  2597. {
  2598.     observe: function(subject, topic, data)
  2599.     {
  2600.     }
  2601. };
  2602. var QuitApplicationObserver =
  2603. {
  2604.     observe: function(subject, topic, data)
  2605.     {
  2606.         fbs.disableDebugger();
  2607.         fbs.shutdown();
  2608.         fbs = null;
  2609.     }
  2610. };
  2611.  
  2612. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2613. function unwrapIValue(object)
  2614. {
  2615.     var unwrapped = object.getWrappedValue();
  2616.     try
  2617.     {
  2618.         if (unwrapped)
  2619.             return XPCSafeJSObjectWrapper(unwrapped);
  2620.     }
  2621.     catch (exc)
  2622.     {
  2623.     }
  2624. }
  2625.  
  2626. //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2627.  
  2628. var consoleService = null;
  2629.  
  2630. function ERROR(text)
  2631. {
  2632.     FBTrace.sysout(text);
  2633.  
  2634.     if (!consoleService)
  2635.         consoleService = ConsoleService.getService(nsIConsoleService);
  2636.  
  2637.     consoleService.logStringMessage(text + "");
  2638. }
  2639.  
  2640. function getExecutionStopNameFromType(type)
  2641. {
  2642.     switch (type)
  2643.     {
  2644.         case jsdIExecutionHook.TYPE_INTERRUPTED: return "interrupted";
  2645.         case jsdIExecutionHook.TYPE_BREAKPOINT: return "breakpoint";
  2646.         case jsdIExecutionHook.TYPE_DEBUG_REQUESTED: return "debug requested";
  2647.         case jsdIExecutionHook.TYPE_DEBUGGER_KEYWORD: return "debugger_keyword";
  2648.         case jsdIExecutionHook.TYPE_THROW: return "interrupted";
  2649.         default: return "unknown("+type+")";
  2650.     }
  2651. }
  2652. // For special chromebug tracing
  2653. function getTmpFile()
  2654. {
  2655.     var file = Components.classes["@mozilla.org/file/directory_service;1"].
  2656.         getService(Components.interfaces.nsIProperties).
  2657.         get("TmpD", Components.interfaces.nsIFile);
  2658.     file.append("fbs.tmp");
  2659.     file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
  2660.     FBTrace.sysout("FBS opened tmp file "+file.path);
  2661.     return file;
  2662. }
  2663.  
  2664. function getTmpStream(file)
  2665. {
  2666.     // file is nsIFile, data is a string
  2667.     var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
  2668.                              createInstance(Components.interfaces.nsIFileOutputStream);
  2669.  
  2670.     // use 0x02 | 0x10 to open file for appending.
  2671.     foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
  2672.     // write, create, truncate
  2673.     // In a c file operation, we have no need to set file mode with or operation,
  2674.     // directly using "r" or "w" usually.
  2675.  
  2676.     return foStream;
  2677. }
  2678.  
  2679. var trackFiles  = {
  2680.     allFiles: {},
  2681.     add: function(fileName)
  2682.     {
  2683.         var name = new String(fileName);
  2684.         this.allFiles[name] = [];
  2685.     },
  2686.     drop: function(fileName)
  2687.     {
  2688.         var name = new String(fileName);
  2689.         this.allFiles[name].push("dropped");
  2690.     },
  2691.     def: function(frame)
  2692.     {
  2693.         var jscontext = frame.executionContext;
  2694.         if (jscontext)
  2695.             frameGlobal = new XPCNativeWrapper(jscontext.globalObject.getWrappedValue());
  2696.  
  2697.         var scopeName = fbs.getLocationSafe(frameGlobal);
  2698.         if (!scopeName)
  2699.             scopeName = "noGlobalObjectLocationInJSContext:"+(jscontext?jscontext.tag:"none");
  2700.  
  2701.         var name = new String(frame.script.fileName);
  2702.         if (! (name in this.allFiles))
  2703.             this.allFiles[name]=["not added"];
  2704.         this.allFiles[name].push(scopeName);
  2705.     },
  2706.     merge: function(moreFiles)
  2707.     {
  2708.         for (var p in moreFiles)
  2709.         {
  2710.             if (p in this.allFiles)
  2711.                 this.allFiles[p] = this.allFiles[p].concat(moreFiles[p]);
  2712.             else
  2713.                 this.allFiles[p] = moreFiles[p];
  2714.         }
  2715.     },
  2716.     dump: function()
  2717.     {
  2718.         var n = 0;
  2719.         for (var p in this.allFiles)
  2720.         {
  2721.             tmpout( (++n) + ") "+p);
  2722.             var where = this.allFiles[p];
  2723.             if (where.length > 0)
  2724.             {
  2725.                 for (var i = 0; i < where.length; i++)
  2726.                 {
  2727.                     tmpout(", "+where[i]);
  2728.                 }
  2729.                 tmpout("\n");
  2730.             }
  2731.             else
  2732.                 tmpout("     none\n");
  2733.  
  2734.         }
  2735.     },
  2736. }
  2737.  
  2738. function tmpout(text)
  2739. {
  2740.     if (!fbs.foStream)
  2741.         fbs.foStream = getTmpStream(getTmpFile());
  2742.  
  2743.     fbs.foStream.write(text, text.length);
  2744.  
  2745. }
  2746.